﻿using System;
using System.IO;
using System.Reflection;
using Pfz.Remoting.Instructions;
using Pfz.Serialization;
using System.Collections.Generic;

namespace Pfz.Remoting.Internal
{
	internal sealed class _ThreadData:
		IDisposable
	{
		private readonly RemotingSerializer Serializer;
		internal readonly IChannel _channel;
		private readonly Stream Stream;

		public _ThreadData(IChannel channel, RemotingClient client)
		{
			_channel = channel;
			Stream = channel.Stream;
			Serializer = new RemotingSerializer();
			Serializer.Context = client;
				
			Serializer.AddDefaultType(typeof(long[]));
			Serializer.AddDefaultType(typeof(Type[]));

			Serializer.AddDefaultType(typeof(_Reference));
			Serializer.AddDefaultType(typeof(_Wrapped));
			Serializer.AddDefaultType(typeof(_WrappedDelegate));
			Serializer.AddDefaultType(typeof(RemotingResult));
			Serializer.AddDefaultType(typeof(_BackObjectReference));
			Serializer.AddDefaultType(typeof(_BackDelegateReference));
		}
		public void Dispose()
		{
			_channel.Dispose();
		}

		public void Serialize(object data)
		{
			Serializer.Serialize(Stream, data);
			Stream.Flush();
		}
		public object Deserialize()
		{
			return Serializer.Deserialize(Stream);
		}

		public int LastDeserializeWrapCount
		{
			get
			{
				return Serializer._wrapCount;
			}
		}

		internal void _Action(Func<object> action)
		{
			var result = new RemotingResult();
			try
			{
				result.Value = action();
			}
			catch(Exception exception)
			{
				if (exception.GetBaseException() is RemotingSyncException)
					throw;

				result.Exception = exception;
			}

			Serialize(result);
		}
		internal void _Action(MethodInfo methodInfo, object[] outParameters, Func<object> action)
		{
			var result = new RemotingResult();
			try
			{
				result.Value = action();
				result.OutValues = RemotingClient._GetOutValues(methodInfo, outParameters);
			}
			catch(Exception exception)
			{
				if (exception.GetBaseException() is RemotingSyncException)
					throw;

				result.Exception = exception;
			}

			Serialize(result);
		}

		private readonly Dictionary<MethodInfo, FastMethodCallDelegate> _methodDelegates = new Dictionary<MethodInfo, FastMethodCallDelegate>();
		internal FastMethodCallDelegate _GetMethodInfoDelegate(MethodInfo methodInfo)
		{
			FastMethodCallDelegate result;
			if (_methodDelegates.TryGetValue(methodInfo, out result))
				return result;

			result = ReflectionHelper.GetMethodCallDelegate(methodInfo);
			_methodDelegates.Add(methodInfo, result);
			return result;
		}
		private readonly Dictionary<PropertyInfo, Func<object, object>> _getPropertyDelegates = new Dictionary<PropertyInfo, Func<object, object>>();
		internal Func<object, object> _GetPropertyGetDelegate(PropertyInfo propertyInfo)
		{
			Func<object, object> result;
			if (_getPropertyDelegates.TryGetValue(propertyInfo, out result))
				return result;

			result = ReflectionHelper.GetPropertyGetterDelegate(propertyInfo);
			_getPropertyDelegates.Add(propertyInfo, result);
			return result;
		}
	}
}
